﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace SLGameEngine
{
    public class ObjectFrames
    {
        public Image[] DirectionFrames;
        public int FramesPerRow = 0;
        public int FrameCount = 0;
    }

    public class ObjectCreature : ObjectBase
    {
        public int Level;

        // Base Stats
        public int Health;          // Total amount of hit points.
        public int Mana;            // Total amount of spell points.
        public int Dexterity;       // Increases changes to dodge.
        public int Strength;        // Increases attack power.
        public int Agility;         // Increases critical hit chance.
        public int Stamina;         // Increase your overall health.
        public int Intelligence;    // Increases spell damage and mana.
        
        // Stats from Gear Worm
        public int Armor;           // Reduces physical damage taken
        public int Resilience;      // Reduces total damage and chance to be criticially hit.
        public int ManaRegen;       // Increases Mana regeneration.

        // Money chances everything.
        public int Gold;
        public int Silver;
        public int Copper;

        public ObjectFrames WalkingFrames   = new ObjectFrames();
        public ObjectFrames FlyingFrames    = new ObjectFrames();
        public ObjectFrames AttackingFrames = new ObjectFrames();
        public ObjectFrames HitFrames       = new ObjectFrames();
        public ObjectFrames DyingFrames     = new ObjectFrames();
        public ObjectFrames IdlingFrames    = new ObjectFrames();
        public ObjectFrames RunningFrames   = new ObjectFrames();
        
        private ObjectFrames _currentObjectFrames;
        private int _currentFrameIndex = 0;
        private Enums.Direction _currentDirection = Enums.Direction.South;
        
        private bool _repeatAnimation = false;
        private Enums.AnimationType _currentAnimation = Enums.AnimationType.Walk;
        DispatcherTimer _timer = new DispatcherTimer();

        private Button NextAnimation = new Button();
        private Button NextDirection = new Button();
        private Button LoopAnimation = new Button();

        private int _speedX = 0;
        private int _speedY = 0;

        public ObjectCreature(double x, double y, int baseObjID)
            : base(x, y, baseObjID)
        {
            _timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
            _timer.Tick += new EventHandler(HeartBeat);

            NextDirection.Content = "D";
            NextDirection.SetValue(Canvas.TopProperty, (double)-20);
            NextDirection.Click += new RoutedEventHandler(NextDirection_Click);
            NextDirection.Visibility = Visibility.Collapsed;
            ToolTipService.SetToolTip((Button)NextDirection, "Next Direction");
            ParentCanvas.Children.Add(NextDirection);

            NextAnimation.Content = "A";
            NextAnimation.SetValue(Canvas.TopProperty, (double)-20);
            NextAnimation.SetValue(Canvas.LeftProperty, (double)20);
            NextAnimation.Click += new RoutedEventHandler(NextAnimation_Click);
            NextAnimation.Visibility = Visibility.Collapsed;
            ToolTipService.SetToolTip((Button)NextAnimation, "Next Animation");
            ParentCanvas.Children.Add(NextAnimation);

            LoopAnimation.Content = "Loop";
            LoopAnimation.SetValue(Canvas.TopProperty, (double)-20);
            LoopAnimation.SetValue(Canvas.LeftProperty, (double)40);
            LoopAnimation.Click += new RoutedEventHandler(LoopAnimation_Click);
            LoopAnimation.Visibility = Visibility.Collapsed;
            ToolTipService.SetToolTip((Button)LoopAnimation, "Loop Animation");
            ParentCanvas.Children.Add(LoopAnimation);
        }

        void NextDirection_Click(object sender, RoutedEventArgs e)
        {
            AnimateNextDirection();
        }

        void NextAnimation_Click(object sender, RoutedEventArgs e)
        {
            AnimateNextAnimation();
        }

        void LoopAnimation_Click(object sender, RoutedEventArgs e)
        {
            _speedX = _speedY = 0;
            AnimateInEditor = !AnimateInEditor;
            Animate(_currentDirection, _currentAnimation, true);
        }

        private void UpdateAnimationFrame()
        {
            int row = _currentFrameIndex / _currentObjectFrames.FramesPerRow;
            int column = _currentFrameIndex - (row * _currentObjectFrames.FramesPerRow);

            _currentObjectFrames.DirectionFrames[(int)_currentDirection].SetValue(Canvas.LeftProperty, (double)-(column*this.Width));
            _currentObjectFrames.DirectionFrames[(int)_currentDirection].SetValue(Canvas.TopProperty, (double)-(row*this.Height));
            if (++_currentFrameIndex == _currentObjectFrames.FrameCount)
            {
                if (true == _repeatAnimation)
                    _currentFrameIndex = 0;
                else
                    _timer.Stop();
            }
        }
       
        void HeartBeat(object sender, EventArgs e)
        {
            if (AnimateInEditor)
            {
                if (_speedX != 0 || _speedY != 0)
                {
                    CurrentX += _speedX;
                    CurrentY += _speedY;
                }

                UpdateAnimationFrame();
            }
        }

        public void SetAnimationDelay(int milliseconds)
        {
            _timer.Interval = new TimeSpan(milliseconds);
        }

        public void Animate(Enums.Direction direction, Enums.AnimationType animationType, bool repeatAnimation)
        {
            _timer.Stop();            

            if (null != _currentObjectFrames && null != _currentObjectFrames.DirectionFrames)
            {
                Image currentImage = _currentObjectFrames.DirectionFrames[(int)_currentDirection];
                if (currentImage != null)
                {
                    if (ObjectCanvas.Children.Contains(currentImage))
                        ObjectCanvas.Children.Remove(currentImage);
                }
            }

            _currentObjectFrames = null;
            _currentFrameIndex = 0;
            
            _currentDirection = direction;
            _currentAnimation = animationType;
            AnimationType.Text = Utils.GetAnimationType(_currentAnimation) + "(" + Utils.GetDirection(_currentDirection) + ")";

            switch (_currentAnimation)
            {
                case Enums.AnimationType.Walk:
                    _currentObjectFrames = WalkingFrames;
                    break;
                case Enums.AnimationType.Fly:
                    _currentObjectFrames = FlyingFrames;
                    break;
                case Enums.AnimationType.Attack:
                    _currentObjectFrames = AttackingFrames;
                    break;
                case Enums.AnimationType.Hit:
                    _currentObjectFrames = HitFrames;
                    break;
                case Enums.AnimationType.Death:
                    _currentObjectFrames = DyingFrames;
                    break;
                case Enums.AnimationType.Idle:
                    _currentObjectFrames = IdlingFrames;
                    break;
                case Enums.AnimationType.Run:
                    _currentObjectFrames = RunningFrames;
                    break;
            }
            if (null != _currentObjectFrames && null != _currentObjectFrames.DirectionFrames)
            {
                if (null != _currentObjectFrames.DirectionFrames[(int)_currentDirection])
                {
                    _repeatAnimation = repeatAnimation;
                    _currentFrameIndex = 0;

                    ObjectCanvas.Children.Add(_currentObjectFrames.DirectionFrames[(int)_currentDirection]);
                    _timer.Start();
                }
            }
        }

        public Enums.AnimationType CurrentAnimation
        {
            get { return _currentAnimation; }
            set
            {
                _currentAnimation = value;
            }
        }

        public Enums.Direction CurrentDirection
        {
            get { return _currentDirection; }
            set
            {
                _currentDirection = value;
            }
        }

        private void LoadImageFrame(ref ObjectFrames frames, int framesPerRow, int frameCount, Enums.Direction direction, string fileName)
        {
            if (null == frames.DirectionFrames)
                frames.DirectionFrames = new Image[8];

            Image img = new Image();
            img.Source = Utils.LoadImage(fileName);
            
            frames.DirectionFrames[(int)direction] = img;
            frames.FramesPerRow = framesPerRow;
            frames.FrameCount = frameCount;
        }
       

        public void AddImageFrame(Enums.Direction direction, Enums.AnimationType animationType, int frameCount, int framesPerRow, string src)
        {
            switch (animationType)
            {
                case Enums.AnimationType.Walk:
                    LoadImageFrame(ref WalkingFrames, framesPerRow, frameCount, direction, src);
                    break;
                case Enums.AnimationType.Fly:
                    LoadImageFrame(ref FlyingFrames, framesPerRow, frameCount, direction, src);
                    break;
                case Enums.AnimationType.Attack:
                    LoadImageFrame(ref AttackingFrames, framesPerRow, frameCount, direction, src);
                    break;
                case Enums.AnimationType.Hit:
                    LoadImageFrame(ref HitFrames, framesPerRow, frameCount, direction, src);
                    break;
                case Enums.AnimationType.Death:
                    LoadImageFrame(ref DyingFrames, framesPerRow, frameCount, direction, src);
                    break;
                case Enums.AnimationType.Idle:
                    LoadImageFrame(ref IdlingFrames, framesPerRow, frameCount, direction, src);
                    break;
                case Enums.AnimationType.Run:
                    LoadImageFrame(ref RunningFrames, framesPerRow, frameCount, direction, src);
                    break;
             
            }          
        }

        public void SetFrames(ref ObjectFrames destFrames, ObjectFrames srcFrames)
        {
            if (null != srcFrames.DirectionFrames)
            {
                destFrames.DirectionFrames = new Image[srcFrames.DirectionFrames.Length];

                for (int i = 0; i < srcFrames.DirectionFrames.Length; i++)
                {
                    string fileName = Utils.GetImageFileName(srcFrames.DirectionFrames[i]);
                    destFrames.DirectionFrames[i] = new Image();
                    destFrames.FramesPerRow = srcFrames.FramesPerRow;
                    BitmapImage bitmapImage = Utils.LoadImage(fileName);
                    destFrames.DirectionFrames[i].Source = bitmapImage;
                    destFrames.FrameCount = srcFrames.FrameCount;
                }
            }
        }

        public void AnimateNextAnimation()
        {
            _currentAnimation++;
            if (_currentAnimation == Enums.AnimationType.End)
                _currentAnimation = Enums.AnimationType.Walk;
            Animate(_currentDirection, (Enums.AnimationType)_currentAnimation, true);
        }

        public void AnimateNextDirection()
        {
            int direction = (int)_currentDirection;
            direction++;
            if (direction == (int)Enums.Direction.End)
                direction = 0;            
            Animate((Enums.Direction) direction, _currentAnimation, true);
        }

        protected override void CopyToClone(int maxRadius, ref ObjectBase clone)
        {
            SetFrames(ref ((ObjectCreature)clone).AttackingFrames, AttackingFrames);
            SetFrames(ref ((ObjectCreature)clone).DyingFrames, DyingFrames);
            SetFrames(ref ((ObjectCreature)clone).FlyingFrames, FlyingFrames);
            SetFrames(ref ((ObjectCreature)clone).HitFrames, HitFrames);
            SetFrames(ref ((ObjectCreature)clone).WalkingFrames, WalkingFrames);
            SetFrames(ref ((ObjectCreature)clone).IdlingFrames, IdlingFrames);
            SetFrames(ref ((ObjectCreature)clone).RunningFrames, RunningFrames);

            base.CopyToClone(maxRadius, ref clone);
        }

        public override ObjectBase Clone(int maxRadius)
        {
            ObjectBase clone = (ObjectBase)new ObjectCreature(0, 0, BaseObjectID);

            CopyToClone(maxRadius, ref clone);

            return clone;
        }

        public override void Deselect()
        {
            AnimationType.Visibility = Visibility.Collapsed;
            NextAnimation.Visibility = Visibility.Collapsed;
            NextDirection.Visibility = Visibility.Collapsed;
            LoopAnimation.Visibility = Visibility.Collapsed;
            SelectionRect.Visibility = Visibility.Collapsed;
        }

        public override ObjectBase Select()
        {
            AnimationType.Visibility = Visibility.Visible;
            NextAnimation.Visibility = Visibility.Visible;
            NextDirection.Visibility = Visibility.Visible;
            LoopAnimation.Visibility = Visibility.Visible;
            SelectionRect.Visibility = Visibility.Visible;

            return (ObjectBase)this;
        }

        public void KeyDown(object sender, KeyEventArgs e)
        {
            SLGameEngine.Enums.Direction nextDirection = SLGameEngine.Enums.Direction.End;
            SLGameEngine.Enums.AnimationType nextAnimation = SLGameEngine.Enums.AnimationType.End;
            bool looping = true;
            bool forcePlay = false;

            switch (e.Key)
            {
                case Key.W:
                    // Move avatar North
                    _speedX = 0;
                    _speedY = -SLGameEngine.Consts.AvatarSpeed;
                    nextDirection = SLGameEngine.Enums.Direction.North;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Walk;
                    break;
                case Key.A:
                    // Move avatar West
                    _speedX = -SLGameEngine.Consts.AvatarSpeed;
                    _speedY = 0;
                    nextDirection = SLGameEngine.Enums.Direction.West;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Walk;
                    break;
                case Key.S:
                    // Move avatar South
                    _speedX = 0;
                    _speedY = SLGameEngine.Consts.AvatarSpeed;
                    nextDirection = SLGameEngine.Enums.Direction.South;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Walk;
                    break;
                case Key.D:
                    // Move avatar East
                    _speedX = SLGameEngine.Consts.AvatarSpeed;
                    _speedY = 0;
                    nextDirection = SLGameEngine.Enums.Direction.East;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Walk;
                    break;
                case Key.Z:
                    _speedX = _speedY = 0;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Idle;
                    nextDirection = CurrentDirection;
                    break;
                case Key.F:
                    _speedX = _speedY = 0;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Attack;
                    nextDirection = CurrentDirection;
                    looping = false;
                    forcePlay = true;
                    Point pt = new Point(CurrentX + CollisionRect.ActualWidth/2 + 10, CurrentY + CollisionRect.ActualHeight/2 + 10);
                    Point selectionPoint = new Point(CurrentX + CollisionRect.ActualWidth/2 - 10, CurrentY + CollisionRect.ActualHeight/2 - 10);
                    ObjectCreature target = (ObjectCreature)ObjectMgr.GetCreatureObjectsCollision(pt, ref selectionPoint);

                    if (target != null)
                    {
                        SLGameEngine.Enums.AnimationType nextTargetAnimation = SLGameEngine.Enums.AnimationType.Hit;
                        if (target.CurrentAnimation == nextTargetAnimation)
                        {
                            nextTargetAnimation = SLGameEngine.Enums.AnimationType.Death;
                        }

                        target.AnimateInEditor = true;
                        target.Animate(target.CurrentDirection, nextTargetAnimation, false);
                    }
                    break;
                case Key.G: // Testing Hit
                    _speedX = _speedY = 0;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Hit;
                    nextDirection = CurrentDirection;
                    looping = false;
                    break;
                case Key.H: // Testing Death
                    _speedX = _speedY = 0;
                    nextAnimation = SLGameEngine.Enums.AnimationType.Death;
                    nextDirection = CurrentDirection;
                    looping = false;
                    break;
            }

            if (forcePlay 
                || ((CurrentAnimation != nextAnimation || CurrentDirection != nextDirection)
                    && nextAnimation != SLGameEngine.Enums.AnimationType.End 
                    && nextDirection != SLGameEngine.Enums.Direction.End))
            {
                Animate(nextDirection, nextAnimation, looping);
            }
        }
    }
}
